﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Windows.Markup;
using System.Reflection;
using System.Data.SqlServerCe;
using System.Data.Linq;
using System.Windows.Threading;
using System.Windows.Interop;

namespace MercuryFE
{


    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Main : Window
    {

        #region fields
        //private MyThumb _selectedItem = null;
        private List<ControlItem> _items;

        private readonly string _defaultPanelName = "DEFAULT";
       // private readonly int _DockWidths = 300; // size of the two side docks
        private string _defaultAssignmentType = "simple";

        //private ResizingAdorner _ra = null;

        private ControlItemInput _currentInput = null;
        private ControlItem _currentItem = null;
        //private bool _itemSelected = false;

        private int? _controlPanelId = null;
        private int? _emulatorId = null;
        private int? _controlPanelAssignmentId = null;
        Popup _popup = null;


        // used to grab directinput
        private Inputs _inputs = null;
        private DispatcherTimer _timer = null;


        // used for assignment
        private readonly string[] availablePlayers = new string[] {
              "P1"
            , "P2"
            , "P3"
            , "P4"
        };

        private readonly string[] availableTags = new string[] {
	         "AD_STICK"
	        ,"BUTTON1"
	        ,"BUTTON2"
	        ,"BUTTON3"
	        ,"BUTTON4"
	        ,"BUTTON5"
	        ,"BUTTON6"
	        ,"BUTTON7"
	        ,"BUTTON8"
	        ,"BUTTON9"
	        ,"BUTTON10"
	        ,"DIAL"
            ,"DIAL_V"
	        ,"JOYSTICK"
	        ,"JOYSTICKLEFT"
	        ,"JOYSTICKRIGHT"
	        ,"LIGHTGUN"
	        ,"PADDLE"
            ,"PADDLE_V"
	        ,"PEDAL"
	        ,"PEDAL2"
	        ,"TRACKBALL"
        };



        AdornerLayer myAdornerLayer = null;
        Adorner myAdorner = null;


        #endregion

        public Main()
        {
           // DynamicLoadStyles();
            InitializeComponent();

            this.Loaded += new RoutedEventHandler(Main_Loaded);
        }

        void Main_Loaded(object sender, RoutedEventArgs e)
        {
            _items = new List<ControlItem>();


            colorSelector.ItemsSource = typeof(Colors).GetProperties();
            colorSelector.DisplayMemberPath = "Name";


            addAddorner();
            loadLayout(); // by default, load it up, since we currently only implement one save (default)
            
        }

        private void addAddorner()
        {
            if (csMain == null)
                return;

            if(myAdornerLayer == null)
                myAdornerLayer = AdornerLayer.GetAdornerLayer(this.csMain);

            if(myAdorner == null)
                myAdorner = new ResizingAdorner(csMain, false, false, true, false);

            myAdornerLayer.Add(myAdorner);
            
         }

        private void removeAddorner()
        {
            if (myAdornerLayer != null && myAdorner != null)
            {
                myAdornerLayer.Remove(myAdorner);
            }
        }

        //private void DynamicLoadStyles()
        //{
        //    string fileName = Environment.CurrentDirectory + @"\ControlPanelItems.xaml";


        //    if (File.Exists(fileName))
        //    {
        //        using (FileStream fs = new FileStream(fileName, FileMode.Open))
        //        {
        //            // Read in ResourceDictionary File
        //            ResourceDictionary dic =
        //               (ResourceDictionary)XamlReader.Load(fs);
        //            Resources.BeginInit();
        //            try
        //            {
        //                // Clear any previous dictionaries loaded
        //                Resources.MergedDictionaries.Clear();
        //                // Add in newly loaded Resource Dictionary
        //                Resources.MergedDictionaries.Add(dic);
        //            }
        //            finally
        //            {
        //                Resources.EndInit();
        //            }
        //        }
        //    }
        //    else
        //        MessageBox.Show("File: " + fileName + " does not exist. Please re-enter the name.");
        //}


        private void btnAddButton_Click(object sender, RoutedEventArgs e)
        {
            addControlItem(ControlItemType.Button);
        }

        private void btnAddJoystick_Click(object sender, RoutedEventArgs e)
        {
            addControlItem(ControlItemType.Joystick);
        }

        private void btnAddTrackball_Click(object sender, RoutedEventArgs e)
        {
            addControlItem(ControlItemType.Trackball);
        }

        private void addControlItem(ControlItemType itemType)
        {
            

            ControlItem t = new ControlItem(itemType);

            DraggableExtender.SetCanDrag(t, true);
            t.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(ControlItemLayout_MouseLeftButtonDown);
            t.MouseRightButtonUp += new MouseButtonEventHandler(t_MouseRightButtonUp);

            
     
            csMain.Children.Add(t);
            Canvas.SetLeft(t, 0);
            Canvas.SetTop(t, 0);

            _items.Add(t);

        }

        private ControlItem selectItem(ControlItem itemToSelect)
        {
            ControlItem selectedItem = null;
            if (_items != null)
            {
                foreach (ControlItem item in _items)
                {
                    if (itemToSelect == item)
                    {
                        item.IsSelected = true;
                        item.Effect = getGlowEffect(Colors.Yellow);
                        selectedItem = item;
                    }
                    else
                    {
                        item.IsSelected = false;
                        item.Effect = null;
                    }
                }
            }
            return selectedItem;
        }

        void ControlItemAssignment_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            // when editing assignment, and left mouse click on an item..
            ControlItem selectedItem = selectItem((ControlItem)sender);
            HandleAssignmentTypeSelection(selectedItem);


        }


        private void HandleAssignmentTypeSelection(ControlItem selectedItem)
        {
            if(_controlPanelAssignmentId != null)
                brdAssignmentProperties.Visibility = Visibility.Visible;

            if (selectedItem.AssignmentType == null)
                selectedItem.AssignmentType = _defaultAssignmentType;


            switch (selectedItem.AssignmentType)
            {
                case "simple": radAssignmentSimple.IsChecked = true;
                    break;
                case "custom": radAssignmentCustom.IsChecked = true;
                    break;
                case "ui": radAssignmentUI.IsChecked = true;
                    break;

            }

            spAssignments.Children.Clear();
            foreach (UIElement element in selectedItem.GetAssignmentSelectors()) //(bool)radAssignmentSimple.IsChecked)
            {
                if ((selectedItem.AssignmentType) == "simple")
                    ((ComboBox)element).SelectionChanged += assignmentSimpleSelectionMade;
                else
                {
                    // custom and ui have stackpanels, with the combo boxes embedded in there...
                    StackPanel sp = (StackPanel)element;
                    foreach (UIElement el in sp.Children)
                    {
                        
                        if(el.GetType() == typeof(ComboBox)) {
                            ((ComboBox)el).SelectionChanged += assignmentSelectionMade;
                        }

                        if (el.GetType() == typeof(TextBox))
                        {
                            ((TextBox)el).TextChanged += assignmentSelectionMade;
                        }
                    }
                }
               spAssignments.Children.Add(element);
            }
        }


        private void assignmentSelectionMade(object sender, RoutedEventArgs e)
        {
            string inputName = "";
            string value = "";

            if (sender.GetType() == typeof(ComboBox))
            {
                ComboBox selector = (ComboBox)sender;
                string objectName = selector.Name;
                inputName = objectName.Substring(3); // skip over 'cmb'
                value = ((KeyValuePair<string, string>)selector.SelectedItem).Key.ToString();
            }
            else if (sender.GetType() == typeof(TextBox))
            {
                TextBox selector = (TextBox)sender;
                string objectName = selector.Name;
                inputName = objectName.Substring(2); // skip over 'tb'
                value = selector.Text;
            }

            // generic method to handle any assignment changes...
            ControlItem selectedItem = (from x in _items where x.IsSelected select x).SingleOrDefault();

            selectedItem.PopulateOtherAssignment(inputName, value);

        }

        private void assignmentSimpleSelectionMade(object sender, RoutedEventArgs e)
        {

            ComboBox selector = (ComboBox)sender;
            StackPanel cont = (StackPanel)selector.Parent;

            // check if there are 'unselected' comboboxes... (return null if true)
            var check = (from x in cont.Children.Cast<ComboBox>() where x.SelectedItem == null select x).SingleOrDefault();

            if (check == null)
            {

                // generic method to handle any assignment changes...
                ControlItem selectedItem = (from x in _items where x.IsSelected select x).SingleOrDefault();

                if (selectedItem != null)
                {
                    List<ComboBox> cmbs = (from x in cont.Children.Cast<ComboBox>() select x).ToList();

                    if(radAssignmentSimple.IsChecked??false)
                        selectedItem.PopulateSimpleAssignment(
                              ((KeyValuePair<string, string>)cmbs[0].SelectedItem).Key
                            , ((KeyValuePair<string, string>)cmbs[1].SelectedItem).Key
                         );
                }
            }
        }

        void ControlItemLayout_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            // make sure the item is selected, and the properties correctly displayed on the side...

            SolidColorBrush selectedColor = null;
            LabelPositionType selectedLabelPos = LabelPositionType.Below;

            ControlItem selectedItem = selectItem((ControlItem)sender);
            selectedColor = selectedItem.Brush;
            selectedLabelPos = selectedItem.LabelPosition;




            // if something selected.... set the properties accordingly
            if (selectedItem != null)
            {
                // make sure properties visible
                if (brdLayoutProperties.Visibility != Visibility.Visible)
                    brdLayoutProperties.Visibility = Visibility.Visible;


                // color selector
                BrushConverter conv = new BrushConverter();
                int i = 0;
                foreach (Object o in colorSelector.Items)
                {
                    PropertyInfo c = o as PropertyInfo;
                    SolidColorBrush brush = conv.ConvertFromString(c.Name) as SolidColorBrush;

                    if (brush.Color.Equals(selectedColor.Color))
                    {
                        colorSelector.SelectedIndex = i;
                         break;
                    }
                    i++;
                }

                // label position
                labelPosSelector.SelectedValue =selectedLabelPos;

                // assigned key
                //tbAssignedKey.Text = "";

                StringBuilder sb = new StringBuilder();
                foreach (ControlItemInput input in selectedItem.Inputs)
                {
                    sb.AppendLine(input.Name + " - " + (input.KeyCode??"[NOT]"));
                }
                tbAssignedKey.Text = sb.ToString();
            }

        }

        private Effect getGlowEffect(Color glowColor)
        {
            DropShadowEffect effect = new DropShadowEffect();
            effect.ShadowDepth = 0;
            effect.BlurRadius = 15;
            effect.Color = glowColor;
            return effect;
        }

        void t_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            // delete item on right mouse click...

            _items.Remove((ControlItem)sender);

            csMain.Children.Remove((ControlItem)sender);

            
            // if no items are actually selected, hide the properties
            if((from x in _items where x.IsSelected select x).SingleOrDefault() == null)
                brdLayoutProperties.Visibility = Visibility.Hidden;
        }

        private void colorSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ControlItem selected = (from x in _items where x.IsSelected select x).SingleOrDefault();

            if (selected != null)
            {

                PropertyInfo c = colorSelector.SelectedItem as PropertyInfo;
                BrushConverter conv = new BrushConverter();
                SolidColorBrush brush = conv.ConvertFromString(c.Name) as SolidColorBrush;

                // fix this up properly with template binding later
                selected.Brush = brush;
                selected.Background = brush;
            }
 
        }

        private void labelPosSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ControlItem selected = (from x in _items where x.IsSelected select x).SingleOrDefault();

            if (selected != null)
            {
                string pos = labelPosSelector.SelectedItem.ToString();

                selected.LabelPosition = (LabelPositionType)Enum.Parse(typeof(LabelPositionType), pos);
            }
 

        }

        private void btnSaveLayout_Click(object sender, RoutedEventArgs e)
        {
            // check if there are any null inputs
            var test = (from x in _items
                        from y in x.Inputs
                        where y.KeyCode == null
                        select y).Take(1).SingleOrDefault();

            if (test != null)
            {
                MessageBox.Show("You have some un-assigned inputs. Please assign inputs before saving.", "Error", MessageBoxButton.OK, MessageBoxImage.Exclamation);
                return;
            }
            
            _controlPanelId = DataLib.saveControlPanelLayout(_defaultPanelName, ref _items, (int)csMain.ActualWidth, (int)csMain.ActualHeight);



        }

        private void btnLoadLayout_Click(object sender, RoutedEventArgs e)
        {

            loadLayout();
        }

        private void loadLayout()
        {
            _items.Clear();
            csMain.Children.Clear();

            // todo: in the future to support multiple layouts (adding/ removing panels)
            int canvasWidth = 0, canvasHeight = 0;
            if (!DataLib.loadControlPanelLayout(_defaultPanelName, ref _items, ref canvasWidth, ref canvasHeight))
                return;


            csMain.Width = canvasWidth;
            csMain.Height = canvasHeight;


            foreach (ControlItem item in _items)
            {

                DraggableExtender.SetCanDrag(item, true);
                item.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(ControlItemLayout_MouseLeftButtonDown);
                item.MouseRightButtonUp += new MouseButtonEventHandler(t_MouseRightButtonUp);

                item.Background = item.Brush;

                csMain.Children.Add(item);
                Canvas.SetLeft(item, item.Position.X);
                Canvas.SetTop(item, item.Position.Y);

            }
        }

        private void btnAssignInputs_Click(object sender, RoutedEventArgs e)
        {
            // clear out all the existing inputs
            foreach (ControlItem item in _items)
            {
                foreach (ControlItemInput input in item.Inputs)
                {
                    input.KeyCode = null;
                }

            }

            brdLayoutProperties.Visibility = Visibility.Hidden;


            // update all the positions for the control items

            foreach (ControlItem item in _items)
                item.Position = item.TranslatePoint(new Point(0.0, 0.0), (Canvas)item.Parent);


            _items.Sort(); // sort them from left to right on the canvas..


            _timer = new DispatcherTimer();
            IntPtr windowHandle = new WindowInteropHelper(this).Handle;


            _inputs = new Inputs(windowHandle, true, false, true); // don't poll mouse..

            _timer.Tick += new EventHandler(_timer_Tick);
            _timer.Interval = new TimeSpan(0,0,0,0, 16); // every 16 ms


            checkForKeyInputs();


        }



        private void checkForKeyInputs()
        {
            foreach (ControlItem item in _items)
            {
                foreach (ControlItemInput input in item.Inputs)
                {
                    if (input.KeyCode == null)
                    {
                        _currentItem = item;
                        _currentItem.Effect = getGlowEffect(Colors.Blue);

                        _currentInput = input;
                        

                        Point pos = item.TranslatePoint(new Point(0.0, 0.0), Window.GetWindow(item));

                        StackPanel sp = new StackPanel();
                        sp.Orientation = Orientation.Vertical;
                        sp.Background = new SolidColorBrush(Colors.Yellow);
                        sp.HorizontalAlignment = HorizontalAlignment.Stretch;

                        Label l = new Label();
                        l.Foreground = new SolidColorBrush(Colors.Black);
                        l.Content = "Press key for " + input.Name;
                        sp.Children.Add(l);

                        _popup = new Popup();
                        _popup.Child = sp;
                        _popup.Width = 150;
                        _popup.Height = 20;
                        _popup.PlacementTarget = item;
                        _popup.IsOpen = true;
                        _popup.StaysOpen = true;


                        _timer.Start();
                        //HookManager.KeyDown += Window1_KeyDown;

              
                        return;
                    }
                }
            }

        }

        void _timer_Tick(object sender, EventArgs e)
        {
            List<InputDeviceState> states = _inputs.pollDevices(true);
            string ItemSubType = "KB";

            string value = "";
            foreach (InputDeviceState ids in states)
            {
                if (!string.IsNullOrEmpty(ids.RawValue))
                {
                    value = ids.RawValue;
                    ItemSubType = ids.InterfaceType;
                    break;
                    //_timer.Stop();
                    //_timer.Dispose();

                    //_inputs.releaseDevices();
                    //_inputs = null;
                    //this.Close();
                }
            }
            if (value != "")
            {
                _timer.Stop();
                _popup.IsOpen = false;
                _popup = null;
                //HookManager.KeyDown -= Window1_KeyDown;
                _currentInput.KeyCode = value;
                _currentInput.AssignmentSubType = ItemSubType;
                 _currentInput = null;

                _currentItem.Effect = null;
                _currentItem = null;

                System.Threading.Thread.Sleep(500); // sleep for half a second
                checkForKeyInputs();
            }
        }

        //void Window1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
        //{
        //    e.Handled = true;

        //    _popup.IsOpen = false;
        //    _popup = null;
        //    HookManager.KeyDown -= Window1_KeyDown;
        //    _currentInput.KeyCode = e.KeyCode.ToString();
        //    _currentInput = null;

        //    _currentItem.Effect = null;
        //    _currentItem = null;

        //    checkForKeyInputs();
        //}

        private void btnNewAssignment_Click(object sender, RoutedEventArgs e)
        {
            NewAssignment na = new NewAssignment();
            na.ShowDialog();

            if (na.assignmentEmulatorID != -1)
            {
                _controlPanelAssignmentId = DataLib.createControlPanelAssignment((int)_controlPanelId, na.assignmentEmulatorID, na.assignmentSystem);

                // reload the selector
                cmbAssignment.ItemsSource = DataLib.getControlPanelAssignments((int)_controlPanelId);
                cmbAssignment.DisplayMemberPath = "Value";

                // if something already loaded, select this item..
                if (_controlPanelAssignmentId != null)
                {
                    for ( int i = 0; i < cmbAssignment.Items.Count; i++) 
                    {
                        KeyValuePair<int, string> option = (KeyValuePair<int, string>)cmbAssignment.Items[i];
                        if (option.Key == _controlPanelAssignmentId)
                        {
                            cmbAssignment.SelectedIndex = i;
                            break;
                        }
                    }
                }
            }

        }

        private void btnSaveAssignment_Click(object sender, RoutedEventArgs e)
        {
            if(_controlPanelAssignmentId != null)
                DataLib.saveControlPanelAssignment((int)_controlPanelAssignmentId, _items);
        }

        private void radEditTypeLayout_Checked(object sender, RoutedEventArgs e)
        {
            // editing layout
            brdAssignment.Visibility = Visibility.Collapsed;
            brdLayout.Visibility = Visibility.Visible;
            brdItems.Visibility = Visibility.Visible;
            if(brdAssignmentProperties != null)
                brdAssignmentProperties.Visibility = Visibility.Collapsed;

            if (_items != null)
            {
                foreach (ControlItem item in _items)
                {
                    DraggableExtender.SetCanDrag(item, true);
                    item.PreviewMouseLeftButtonDown += ControlItemLayout_MouseLeftButtonDown;
                    item.PreviewMouseLeftButtonDown -= ControlItemAssignment_MouseLeftButtonDown;
                    item.MouseRightButtonUp += new MouseButtonEventHandler(t_MouseRightButtonUp);
                }
            }
            addAddorner();
            selectItem(null);
        }

        private void radEditTypeAssignment_Checked(object sender, RoutedEventArgs e)
        {
            // save the layout first
            // check if there are any null inputs
            var test = (from x in _items
                        from y in x.Inputs
                        where y.KeyCode == null
                        select y).SingleOrDefault();

            if (test != null)
            {
                MessageBox.Show("You have some un-assigned inputs. Please assign inputs before continuning.", "Error", MessageBoxButton.OK, MessageBoxImage.Exclamation);
                radEditTypeLayout.IsChecked = true;
                return;
            }


            _controlPanelId = DataLib.saveControlPanelLayout(_defaultPanelName, ref _items, (int)csMain.ActualWidth, (int)csMain.ActualHeight);





            brdAssignment.Visibility = Visibility.Visible;
            brdLayout.Visibility = Visibility.Collapsed;
            brdItems.Visibility = Visibility.Collapsed;
            brdLayoutProperties.Visibility = Visibility.Collapsed;

            foreach (ControlItem item in _items)
            {
                DraggableExtender.SetCanDrag(item, false);
                item.PreviewMouseLeftButtonDown -= ControlItemLayout_MouseLeftButtonDown;
                item.PreviewMouseLeftButtonDown += ControlItemAssignment_MouseLeftButtonDown;
                item.MouseRightButtonUp -= new MouseButtonEventHandler(t_MouseRightButtonUp);
            }
            removeAddorner();
            selectItem(null);


            // editing Assignments
            // load available assignments
            populateAssigmentSelector();

        }

        private void populateAssigmentSelector()
        {
            // used for drop down assignment selector...
            Dictionary<int, string> assignments = DataLib.getControlPanelAssignments((int)_controlPanelId);
            cmbAssignment.ItemsSource = assignments;
            cmbAssignment.DisplayMemberPath = "Value";

            // select the first assignment found
            if (assignments.Count > 0)
                cmbAssignment.SelectedIndex = 0;
            else
                cmbAssignment.SelectedItem = null;
        }

        private void cmbAssignment_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // load the assignments, if any, from the DB..
            if (cmbAssignment.SelectedItem != null)
            {
                _controlPanelAssignmentId = ((KeyValuePair<int, string>)cmbAssignment.SelectedItem).Key;

                if (_controlPanelAssignmentId != null)
                {
                    bool isMame = false;
                    DataLib.loadControlPanelAssignment((int)_controlPanelAssignmentId, ref _items, ref isMame, ref _emulatorId);

                    // if we are using a mame emulator, then set default assignment selection to simple, otherwise custom (eg for sega, etc)
                    if (isMame)
                    {
                        _defaultAssignmentType = "simple";
                        btnExportMameConfig.IsEnabled = true;
                    }
                    else
                    {
                        _defaultAssignmentType = "custom";
                        btnExportMameConfig.IsEnabled = false;
                    }
                }

                btnSaveAssignment.IsEnabled = true;
                btnDeleteAssignment.IsEnabled = true;
            }
            else
            {
                _controlPanelAssignmentId = null;
                btnSaveAssignment.IsEnabled = false;
                btnDeleteAssignment.IsEnabled = false;
            }
        }



        private void btnDeleteAssignment_Click(object sender, RoutedEventArgs e)
        {
            if (_controlPanelAssignmentId != null)
                DataLib.deleteControlPanelAssignment((int)_controlPanelAssignmentId);

            populateAssigmentSelector();
        }



        private void radAssignmentSimple_Checked(object sender, RoutedEventArgs e)
        {
            HandleAssignmentChange("simple");
        }

        private void radAssignmentUI_Checked(object sender, RoutedEventArgs e)
        {
            HandleAssignmentChange("ui");
        }

        private void radAssignmentCustom_Checked(object sender, RoutedEventArgs e)
        {
            HandleAssignmentChange("custom");
        }

        private void HandleAssignmentChange(string assignmentType)
        {
            if (_items != null)
            {
                ControlItem selected = (from x in _items where x.IsSelected select x).SingleOrDefault();

                if (selected != null)
                {
                    if (selected.AssignmentType != assignmentType)
                    {
                        selected.AssignmentType = assignmentType;
                        foreach (ControlItemInput cii in selected.Inputs)
                        {
                            cii.Assignment = null;
                            cii.AssignmentType = assignmentType;
                        }
                    }
                }

                HandleAssignmentTypeSelection(selected);
            }
        }

        private void btnExportMameConfig_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.Forms.SaveFileDialog fld = new System.Windows.Forms.SaveFileDialog();
            fld.Filter = "Config file|*.cfg";
   
            if (fld.ShowDialog() == System.Windows.Forms.DialogResult.OK)
			{
                string fileName = fld.FileName;

                DataLib.generateMameConfig(fileName, (int)_emulatorId, (int)_controlPanelId);
                MessageBox.Show(fileName + " saved.");
			}

        }



    }





}
